#include "StdAfx.h"
#include "ResynthCarrier.h"
#include "SoundBank.h"
#include "WaveFormProvider.h"
#include "AmplitudeIndex.h"

#include <set>


ResynthCarrier::ResynthCarrier(unsigned int samplingRate, SoundBank* soundBank)
{
	this->samplingRate = samplingRate;
	this->soundBank = soundBank;
	vocoderListener = NULL;
}

ResynthCarrier::~ResynthCarrier(void)
{
	std::map<int, WaveFormProvider*>::iterator it = waveFormProviders.begin();
	for(; it!=waveFormProviders.end(); it++)
	{
		delete it->second;
		it->second = NULL;
	}
}

void ResynthCarrier::SetVocoderListener(VocoderListener* vocoderListener)
{
	this->vocoderListener = vocoderListener;
}

short* ResynthCarrier::CreateCarrierSample(Settings* settings, short* modulatorSample, unsigned int length, std::vector<SampleMarker>* markers)
{
	if(mySample.GetLength()==0)
	{
		mySample.SetLength(length);
	}
	fft.DoFFT(modulatorSample, length);	
	Complex* complex = fft.GetComplexOut();
	fft.Normalize(complex, length);

	short* myData = CreateCarrierSampleWithProcessors();
	//short* myData = CreateCarrierSampleWithSineWaves();
	
	ApplyStopConsonants(modulatorSample, myData, length, markers);
	return myData;
}

short* ResynthCarrier::CreateCarrierSampleWithProcessors()
{
	Complex* complex = fft.GetComplexOut();
	unsigned int length = fft.GetArrayLength();
	//SortedAmplitudes sortedAmplitudes(complex, length);
	for(unsigned int i=0; i<fftProcessors.size(); i++)
	{
		complex = fftProcessors[i]->Process(complex, length, NULL/*&sortedAmplitudes*/);
	}
	short* myData = mySample.GetData();
	fft.InvFFT(complex, myData, length);
	return myData;
}

short* ResynthCarrier::CreateCarrierSampleWithSineWaves()
{
	Complex* complex = fft.GetComplexOut();
	SineWaveFromComplexFFT(complex, fft.GetArrayLength());
	return mySample.GetData();
}

/*
short* ResynthCarrier::CreateCarrierSampleWithDampenedBins()
{
}
*/

void ResynthCarrier::SineWaveFromComplexFFT(Complex* complex, unsigned int length)
{
	double multiplier = 1;

	SortedAmplitudes sortedAmplitudes(complex, length);

	//sort by amplitude (and keep index) in first half of fft array
	std::vector<AmplitudeIndex> amps;
	sortedAmplitudes.GetHighestAmplitudes(100, amps);

	double indexFrequencyWidth = ((double)(samplingRate/2)/*frequency resolution after fft*/)/((double)(length/2)/*number of frequency bins, second half of buffer is ignored*/);
	double total = 0;
	std::vector<int> frequencies;
	std::set<int> smearedIdx;


	for(unsigned int i=0; i<amps.size(); i++)
	{
		//if(smearedIdx.count(i)>0)
		//	continue;
		if(amps[i].index % amps[0].index || amps[i].index==0)
			continue;//smearing

		total += amps[i].amplitude;
		
		//calculate the frequency for current fft index
		double dFreq = indexFrequencyWidth * amps[i].index + (indexFrequencyWidth/2);

		/*
		//check out if we have smearing later on
		for(unsigned int j=0; j<amps.size(); j++)
		{
			if(amps[j].index==amps[i].index+1)
			{
				//shift up by half
				dFreq += (indexFrequencyWidth/2);
				smearedIdx.insert(j);
			}
			else if(amps[j].index==amps[i].index-1)
			{
				//shift down by half
				dFreq -= (indexFrequencyWidth/2);
				smearedIdx.insert(j);
			}
		}
		*/

		int frequency = (int)(dFreq*multiplier);		
		if(frequency > samplingRate || frequency <= 0) continue;
		frequencies.push_back(frequency);
	}

	std::vector<short*> samples;
	std::vector<double> intensities;
	for(unsigned int i=0; i<frequencies.size(); i++)
	{
		WaveFormProvider* waveFormProvider = GetWaveFormProvider(frequencies[i]);
		short* sample = new short[length];
		waveFormProvider->GetData(sample, length);
		double intensity = amps[i].amplitude/total;
		samples.push_back(sample);
		intensities.push_back(intensity);
	}
	mySample.Mix(samples, intensities, length);
	for(unsigned int i=0; i<samples.size(); i++)
	{
		delete samples[i];
	}
}

WaveFormProvider* ResynthCarrier::GetWaveFormProvider(int frequency)
{
	WaveFormProvider* provider = NULL;
	if(waveFormProviders.count(frequency)>0)
		provider = waveFormProviders[frequency];
	else
	{
		provider = new WaveFormProvider(Sine, soundBank, frequency, 1.0);
		waveFormProviders[frequency] = provider;
	}
	return provider;
}